#include "fsm.h"
#include "deployment.h"
#include <memory>
#include <string>
#include <stdexcept>

using namespace std;

//Constructor
FSM::FSM() {
	currentState = 0;
	delayState = "NONE";
	deployment = 0;
}

FSM::FSM(string _name) {
	currentState = 0;
	delayState = "NONE";
	deployment = new Deployment(_name);
}

FSM::FSM(Deployment * _dep) {
	if ( !_dep ) throw invalid_argument("FSM::FSM(Deployment*)");

	currentState = 0;
	delayState = "NONE";
	deployment = _dep;
}

//Destructor
FSM::~FSM() {
	stateBank.clear();
	delete deployment;
}

//Update each tick
int FSM::update() {
	//Make sure a current state is loaded
	if (! currentState) throw runtime_error("FSM::Update(const float&)");

	//Check if there are any delayed transition requests
	if (delayState != "NONE") {
		transitionTo(delayState);
		delayState = "NONE";
	}

	//Update the current state, may trigger a transition.
	return currentState->update();
}

//Called to transition to another state
//@param stateName the name of the state to transition to
void FSM::transitionTo(std::string stateName, int param) {
	//Find the named state
	FSMState *goToState = 0;
	for(iter= stateBank.begin(); iter!= stateBank.end(); iter++)
		if ( (*iter)->stateName == stateName )
			goToState = iter->get();

	//Error, trying to transition to a non-existant state
	if (!goToState) {
		//Print an error here, or assert if you want
		throw runtime_error("FSM::TransitionTo(string)");
	}

	history.push_back(currentState->stateName);

	currentState->doExit();
	goToState->doEnter(param);
	currentState = goToState;
}

void FSM::transitionBack() {
	string stateName = history.back();
	history.pop_back();

	//Find the named state
	FSMState *goToState = 0;
	for(iter= stateBank.begin(); iter!= stateBank.end(); iter++)
		if ( (*iter)->stateName == stateName )
			goToState = iter->get();

	//Error, trying to transition to a non-existant state
	if (!goToState) {
		//Print an error here, or assert if you want
		throw runtime_error("FSM::TransitionTo(string)");
	}

	// Doesn't need to do the doEnter()
	currentState->doExit();
	currentState = goToState;
}

//Transition to another state (delayed until beginning of next update)
//@param stateName the name of the state to transition to
void FSM::delayTransitionTo(std::string stateName) {
	delayState = stateName;
}

//Add a state to the bank, optionally make it the current state
//@param newState the new state to add to the state machine
//@param makeCurrent is this new state the current state?
void FSM::addState(FSMState *newState, bool makeCurrent) {
	//Add this state to the FSM
	std::shared_ptr<FSMState> newStatePtr(newState);
	stateBank.push_back(newStatePtr);
	//Make this the current state?
	if (makeCurrent) currentState = newState;
}

//What is the name of the current state?
std::string FSM::getState() {
	return currentState->stateName;
}

Deployment ** FSMState::getDeployment() {
	if (fsm == 0) throw runtime_error("FSMState::getDeployment()");
	return &(fsm->deployment);
}

TemplateSet ** FSMState::getSet() {
	Deployment * d = *( getDeployment() );
	if ( !d ) throw runtime_error("FSMState::getSet()");
	return &(d->ownTemplateSet);
}
